/*
 * Copyright (c) 2013-2018 上海汇数数据. All rights reserved.
 * @(#) Base64Kt.java 2018-08-05 22:06
 */

package cn.jh.common.core.base;

import com.google.common.base.Charsets;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.util.Base64;


/**
 * Base64 解码与编码工具。
 *
 * @author Fuchun
 * @since 1.0
 */
public class Base64Kt {

    private static final Logger LOGGER = LoggerFactory.getLogger(Base64Kt.class);
    private static final Base64.Encoder URL_SAFE = Base64.getUrlEncoder().withoutPadding();
    static final byte[] EMPTY_BYTES = new byte[0];

    @Contract("null -> null")
    public static String encode(byte[] data) {
        return encode(data, false);
    }

    @Contract("null, _ -> null")
    public static String encode(byte[] data, boolean urlSafe) {
        if (data == null) return null;
        if (data.length == 0) return "";
        if (urlSafe) {
            return URL_SAFE.encodeToString(data);
        } else {
            return Base64.getEncoder().encodeToString(data);
        }
    }

    @Contract("null -> null")
    public static String encodeUrlSafe(byte[] data) {
        return encode(data, true);
    }

    @Contract("null, _ -> null")
    public static String encodeUrlSafe(String data, Charset charset) {
        if (data == null) return null;
        if (data.isEmpty()) return "";
        byte[] bytes = data.getBytes(charset == null ? Charsets.UTF_8 : charset);
        return encodeUrlSafe(bytes);
    }

    @Contract("null, _, _ -> null")
    public static String encode(String data, Charset charset, boolean urlSafe) {
        if (data == null) return null;
        if (data.isEmpty()) return "";
        Charset c = MoreObjects.firstNonNull(charset, Charsets.UTF_8);
        byte[] bytes = data.getBytes(c);
        return encode(bytes, urlSafe);
    }

    @Contract("null, _ -> null")
    public static String encode(String data, boolean urlSafe) {
        return encode(data, Charsets.UTF_8, urlSafe);
    }

    @Contract("null -> null")
    public static String encode(String data) {
        return encode(data, false);
    }

    @Contract("null -> null")
    public static String encodeToMime(byte[] data) {
        if (data == null) return null;
        if (data.length == 0) return "";
        return Base64.getMimeEncoder().encodeToString(data);
    }

    /**
     * 使用 `Base64` 的编码方案将 `Base64` 编码的字符串解码为新分配的字节数组。
     *
     * ```
     * Base64Kt.decode(null)    =    ByteArray(0)
     * Base64Kt.decode("")      =    ByteArray(0)
     * Base64Kt.decode("  ")    =    ByteArray(0)
     * Base64Kt.decode("asdfg") =    null;  表示解码错误
     * ```
     *
     * @param src 要解码的字符串
     * @return 返回一个新分配的包含解码字节的字节数组。
     */
    @Nullable
    public static byte[] decode(String src) {
        if (Strings.isNullOrEmpty(src) || src.trim().isEmpty()) {
            return EMPTY_BYTES;
        }
        Base64.Decoder decoder;
        if (src.contains("\r\n") || src.contains("\n")) {
            decoder = Base64.getMimeDecoder();
        } else if (src.contains("-") || src.contains("_")) {
            decoder = Base64.getUrlDecoder();
        } else {
            decoder = Base64.getDecoder();
        }
        try {
            return decoder.decode(src);
        } catch (IllegalArgumentException ex) {
            String message = ex.getMessage();
            if (message == null) message = ex.getClass().getSimpleName();
            LOGGER.warn("\"{}\" base64 decode error: {}", src, message);

            return null;
        }
    }

    @Nullable
    public static String decodeToString(String src) {
        return decodeToString(src, Charsets.UTF_8);
    }

    @Nullable
    public static String decodeToString(String src, Charset charset) {
        byte[] bytes = decode(src);
        if (bytes == null) return null; // 发生解码错误
        if (bytes.length == 0) return "";
        return new String(bytes, charset == null ? Charsets.UTF_8 : charset);
    }

    private Base64Kt(){}
}
